Proof of concept: task eviction after snapshot for turbo-tasks-backend#91790
Merged
Conversation
Contributor
Author
This stack of pull requests is managed by Graphite. Learn more about stacking. |
Contributor
Tests Passed |
Contributor
Stats from current PR✅ No significant changes detected📊 All Metrics📖 Metrics GlossaryDev Server Metrics:
Build Metrics:
Change Thresholds:
⚡ Dev Server
📦 Dev Server (Webpack) (Legacy)📦 Dev Server (Webpack)
⚡ Production Builds
📦 Production Builds (Webpack) (Legacy)📦 Production Builds (Webpack)
📦 Bundle SizesBundle Sizes⚡ TurbopackClient Main Bundles
Server Middleware
Build DetailsBuild Manifests
📦 WebpackClient Main Bundles
Polyfills
Pages
Server Edge SSR
Middleware
Build DetailsBuild Manifests
Build Cache
🔄 Shared (bundler-independent)Runtimes
📎 Tarball URL |
Merging this PR will not alter performance
Comparing Footnotes
|
c194c61 to
fa50df9
Compare
fa50df9 to
056be10
Compare
lukesandberg
commented
Mar 24, 2026
lukesandberg
commented
Mar 24, 2026
lukesandberg
commented
Mar 24, 2026
lukesandberg
commented
Mar 24, 2026
lukesandberg
commented
Mar 24, 2026
lukesandberg
commented
Mar 24, 2026
lukesandberg
commented
Mar 24, 2026
lukesandberg
commented
Mar 24, 2026
lukesandberg
commented
Mar 24, 2026
lukesandberg
commented
Mar 24, 2026
116d63d to
ae6878d
Compare
35da0fa to
290c8d7
Compare
890a1ba to
da87628
Compare
4378987 to
d2bde2d
Compare
c5e00ef to
327ffb1
Compare
Contributor
Merge activity |
327ffb1 to
94bd15c
Compare
sokra
reviewed
May 8, 2026
sokra
approved these changes
May 8, 2026
f0196da to
40d8578
Compare
The macro emitted `if !drop_partial() { reset; }` for filter_transient
inline fields, which destroyed transient residue (HasResidue branch
unconditionally cleared the field). After eviction the upper-edge to a
transient HMR root was wiped, so dirty propagation from a file change
never reached an active root and HMR didn't fire.
Replace the bool return with `DropPartialOutcome { Empty, HasResidue }`,
make `DropPartial` a pub(crate) trait that CellData also implements
(was duck-typed before), and have `TaskStorage::drop_partial` return
the outcome authoritatively — the eviction caller no longer needs a
separate `is_empty()` query. Add per-category `is_empty_data` /
`is_empty_meta` / `is_empty_transient` helpers so the final
emptiness check skips the categories the drop just cleared.
if updating state raced with a snapshot it could trigger a deadlock due to the inner lock inside of state managing the set of invalidators
40d8578 to
659b6b6
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.

Summary
Implements memory eviction for the turbo-tasks engine. After a persistence snapshot completes, tasks that are safe to remove are evicted from in-memory storage and transparently restored from disk on next access.
Eviction levels
current_session_clean, aggregated session-clean counts).Data and meta evictability are computed independently — if one category is modified but the other is clean, the clean category can still be dropped.
Eviction is gated behind
BackendOptions::evict_after_snapshot(off by default), and can be enabled in Next.js via theTURBO_ENGINE_EVICT_AFTER_SNAPSHOT=1env var for testing.Key changes
Orthogonal eviction decision tree (
storage_schema.rs): Data and meta evictability are computed independently. Full eviction additionally requires no meaningful transient state (session-clean flags, aggregated session-clean counts). Replaces the previous sequential bail-out approach which was too aggressive on full eviction (losing transient session state on leaf tasks) and not aggressive enough on partial eviction (blocking all eviction when only one category was modified).drop_partial()codegen (task_storage_macro.rs): New generated methods to drop datarestore_from_*()codegen changes (task_storage_macro.rs): New semantics for merging persistent data from the backend with transient data stored in memory.task_cachemoved intoStorage(storage.rs): TheCachedTaskType → TaskIddeduplication map was previously a separate field onTurboTasksBackendInner. It is now owned byStorageso eviction can remove entries when a task is fully evicted. Becausetask_cacheis a pure performance cache (entries are re-populated bytask_by_type()on miss once the task type is persisted to backing storage), evicting entries is safe. After bulk eviction the map is shrunk when it is less than half full.Parallel shard eviction (
storage.rs): Eviction iterates all storage shards in parallel after snapshot, applying the appropriate eviction level per task. Each shard is shrunk after bulk eviction to reclaim slack capacity.Design notes
current_session_cleanis set we prevent full eviction to avoid rechecking. Within a session the file-watchers are responsible for invalidations after settingcurrent_session_clean.Known limitations (proof of concept)